package top.v5it.japi.plus.support.idcenter.impl;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import org.springframework.data.redis.core.RedisTemplate;
import top.v5it.japi.plus.support.LuaScriptUtil;
import top.v5it.japi.plus.support.idcenter.IdCenter;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.stream.IntStream;

/**
 * 基于redis生成id
 *
 * @author zhanpu
 * @date 2021/3/14
 */
public class RedisGenerateIdCenter implements IdCenter {

    private final RedisTemplate<String, Object> redisTemplate;
    private static final int MAX_LENGTH = 19;
    private static final Map<Integer, Long> LENGTH_MAP;

    static {
        final Map<Integer, Long> lengthMap = new HashMap<>(MAX_LENGTH);
        IntStream
                .iterate(1, v -> v + 1)
                .limit(18)
                .forEach(v -> lengthMap.put(v, max(v)));

        LENGTH_MAP = MapUtil.unmodifiable(lengthMap);
    }

    public RedisGenerateIdCenter(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * 根据长度，计算最大值
     *
     * @param length
     * @return
     */
    private static Long max(int length) {

        if (length == MAX_LENGTH) {
            return Long.MAX_VALUE;
        }

        String str = StrUtil.fillAfter("", '9', length);

        return Long.valueOf(str);
    }

    /**
     * 字符前面拼装0
     *
     * @param nextId
     * @param length
     * @return
     */
    private static String fillBefore(long nextId, int length) {
        return StrUtil.fillBefore("" + nextId, '0', length);
    }

    @Override
    public long nextId() {

        String defaultIdKey = "japi:generateid";
        return nextId(defaultIdKey, MAX_LENGTH);
    }

    /**
     * 获取序列，采用redis生成
     *
     * @param key
     * @param length
     * @return
     */
    @Override
    public long nextId(String key, int length) {
        Assert.notBlank(key);
        Assert.checkBetween(length, 1, 19);

        Long result = redisTemplate.execute(LuaScriptUtil.getLuaIncrseqScript(), Collections.singletonList(key), LENGTH_MAP.get(length).toString());
        return Optional
                .ofNullable(result)
                .orElse(0L);
    }

    /**
     * 生成id串，长度为length
     *
     * @param key
     * @param length
     * @return
     */
    @Override
    public String nextIdStr(String key, int length) {
        return fillBefore(nextId(key, length), length);
    }

}
